/*******************************************************************************
 * Copyright (c) 2011 Irineu Martins de Lima Moura (imlm2).
 * This file is part of JBeat.
 * 
 *     JBeat is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU Lesser General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 * 
 *     JBeat is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU Lesser General Public License for more details.
 * 
 *     You should have received a copy of the GNU Lesser General Public License
 *     along with JBeat.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * Contributors:
 *     Irineu Martins de Lima Moura (imlm2) - initial API and implementation
 ******************************************************************************/
package br.ufpe.cin.imlm2.jbeat.soot.common;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import soot.MethodOrMethodContext;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.jimple.toolkits.callgraph.ReachableMethods;
import soot.util.Chain;
import soot.util.queue.QueueReader;

/**
 * A wrapper for the soot Scene with facility methods to query for SootClass and
 * SootMethod objects and other utility methods. Collections returned by the
 * utility methods may be safely modified as any changes made will not be
 * reflected in the Soot Scene itself.
 * 
 * @author irineu
 */
public class SceneWrapper {

	/**
	 * Returns all the concrete classes found in the Scene.
	 * 
	 * @return
	 */
	public static Collection<SootClass> getConcreteClasses() {
		Chain<SootClass> allClasses = Scene.v().getClasses();
		Set<SootClass> classes = new HashSet<SootClass>();
		for ( SootClass sootClass : allClasses ) {
			if ( sootClass.isConcrete() ) {
				classes.add( sootClass );
			}
		}
		return classes;
	}

	/**
	 * Returns all <code>SootClass</code> objects in the Scene that represent
	 * concrete and abstract classes but not interfaces.
	 * 
	 * @return
	 */
	public static Collection<SootClass> getClasses() {
		Chain<SootClass> allClasses = Scene.v().getClasses();
		Set<SootClass> classes = new HashSet<SootClass>();
		for ( SootClass sootClass : allClasses ) {
			if ( !sootClass.isInterface() ) {
				classes.add( sootClass );
			}
		}
		return classes;
	}

	/**
	 * Return all the abstract classes (that are not interfaces) in the Scene.
	 * 
	 * @return
	 */
	public static Collection<SootClass> getAbstractClasses() {
		Chain<SootClass> allClasses = Scene.v().getClasses();
		Set<SootClass> classes = new HashSet<SootClass>();
		for ( SootClass sootClass : allClasses ) {
			if ( !sootClass.isConcrete() && !sootClass.isInterface() ) {
				classes.add( sootClass );
			}
		}
		return classes;
	}

	/**
	 * Return all classes (concrete and abstract) and interfaces in the Scene.
	 * 
	 * @return
	 */
	public static Collection<SootClass> getAllClassesAndInterfaces() {
		return new HashSet<SootClass>( Scene.v().getClasses() );
	}

	/**
	 * Returns all application classes (concrte and abstract) and interfaces in
	 * the Scene.
	 * 
	 * @return
	 */
	public static Collection<SootClass> getAllApplicationClassesAndInterfaces() {
		return new HashSet<SootClass>( Scene.v().getApplicationClasses() );
	}

	/**
	 * Returns all library classes (concrete and abstract) and interfaces in the
	 * Scene.
	 * 
	 * @return
	 */
	public static Collection<SootClass> getAllLibraryClassesAndInterfaces() {
		return new HashSet<SootClass>( Scene.v().getLibraryClasses() );
	}

	/**
	 * Returns all methods (concrete or abstracts) in the Scene.
	 * 
	 * @return
	 */
	public static Collection<SootMethod> getAllMethods() {
		List<SootMethod> allMeths = new ArrayList<SootMethod>();
		Collection<SootClass> classes = SceneWrapper.getAllClassesAndInterfaces();
		for ( SootClass sootClass : classes ) {
			allMeths.addAll( sootClass.getMethods() );
		}
		return allMeths;
	}

	/**
	 * Returns all the concrete methods in the Scene.
	 * 
	 * @return
	 */
	public static Collection<SootMethod> getConcreteMethods() {
		Collection<SootMethod> allMeths = getAllMethods();
		Iterator<SootMethod> it = allMeths.iterator();
		while ( it.hasNext() )
			if ( !it.next().isConcrete() )
				it.remove();
		return allMeths;
	}

	/**
	 * Returns all reachable methods found so far that are in the Scene.
	 * 
	 * @return
	 */
	public static Collection<SootMethod> getReachableMethods() {
		Collection<SootMethod> reachableMeths = new ArrayList<SootMethod>();
		ReachableMethods reachables = Scene.v().getReachableMethods();
		QueueReader<MethodOrMethodContext> reader = reachables.listener();
		while ( reader.hasNext() ) {
			reachableMeths.add( reader.next().method() );
		}
		return reachableMeths;
	}

	/**
	 * Returns all methods from the Java Standard Library classes and
	 * interfaces.
	 * 
	 * @return
	 */
	public static Collection<SootMethod> getLibraryMethods() {
		Collection<SootMethod> libraryMethods = new ArrayList<SootMethod>();
		Collection<SootClass> libraryClasses = getAllLibraryClassesAndInterfaces();
		for ( SootClass sootClass : libraryClasses ) {
			for ( SootMethod sm : sootClass.getMethods() ) {
				libraryMethods.add( sm );
			}
		}
		return libraryMethods;
	}

	/**
	 * Returns all methods from the application classes and interfaces.
	 */
	public static Collection<SootMethod> getApplicationMethods() {
		Collection<SootMethod> applicationMethods = new ArrayList<SootMethod>();
		Collection<SootClass> applicationClasses = getAllApplicationClassesAndInterfaces();
		for ( SootClass sootClass : applicationClasses ) {
			for ( SootMethod meth : sootClass.getMethods() ) {
				applicationMethods.add( meth );
			}
		}
		return applicationMethods;
	}

}
